package club.zhaoliang.study.app;

import android.app.Application;
import android.content.Context;
import android.os.Environment;
import android.util.Log;

import com.github.moduth.blockcanary.BlockCanary;
import com.squareup.leakcanary.LeakCanary;
import com.squareup.leakcanary.RefWatcher;

import org.json.JSONObject;

import java.io.File;
import java.io.FileWriter;

import club.zhaoliang.study.blockcanary.DemoBlockCanaryContext;
import xcrash.ICrashCallback;
import xcrash.TombstoneManager;
import xcrash.TombstoneParser;
import xcrash.XCrash;


public class DemoApplication extends Application {

    private RefWatcher refWatcher;
    private String TAG = "DemoApplication";

    @Override
    public void onCreate() {
        super.onCreate();
        refWatcher = setupLeakCanary();
        initXcrash();
        initBlockCanary();
    }

    private void initBlockCanary() {
        BlockCanary.install(this, new DemoBlockCanaryContext()).start();

    }


    private void initXcrash() {

        // callback for java crash, native crash and ANR
        ICrashCallback callback = new ICrashCallback() {
            @Override
            public void onCrash(String logPath, String emergency) {
                Log.d(TAG, "log path: " + (logPath != null ? logPath : "(null)") + ", emergency: " + (emergency != null ? emergency : "(null)"));

                if (emergency != null) {
                    debug(logPath, emergency);

                    // Disk is exhausted, send crash report immediately.
//                    sendThenDeleteCrashLog(logPath, emergency);
                } else {
                    // Add some expanded sections. Send crash report at the next time APP startup.

                    // OK
                    TombstoneManager.appendSection(logPath, "自定义key1", "自定义value1");
                    TombstoneManager.appendSection(logPath, "自定义key2", "自定义value2");

                    // Invalid. (Do NOT include multiple consecutive newline characters ("\n\n") in the content string.)
                    // TombstoneManager.appendSection(logPath, "expanded_key_3", "expanded_content_row_1\n\nexpanded_content_row_2");

                    debug(logPath, null);
                }
            }
        };


        Log.d(TAG, "xCrash SDK init: start");

        //自定义目录
        String logDir = Environment.getExternalStorageDirectory() + "/leniu/xcrash/";

        // 自定义参数配置详解：https://blog.csdn.net/cxmfzu/article/details/102624295

        // Initialize xCrash.
        XCrash.init(this, new XCrash.InitParameters()
                //------------------------------通用输出配置------------------------------
                .setAppVersion("study_demo_1.0.0") //app应用版本信息
                .setLogDir(logDir)//用于存放异常日志的文件夹
                .setLogFileMaintainDelayMs(1000)//初始化xCrash后延迟xx毫秒，进行日志文件维护任务（为清理多余的过期日志文件）
                //.setLogger(null) //xCrash库的日志输出接口
                //.setLibLoader(null)//xCrash库需要加载的so文件路径
                //------------------------------通用输出配置------------------------------
                //------------------------------Java异常输出配置------------------------------
                //.enableJavaCrashHandler()//使能Java异常处理器（默认）
                //.disableJavaCrashHandler()//不使能Java异常处理器
                .setJavaLogCountMax(10)//java异常文件数量，超过10个会自动删除第一个
                .setJavaRethrow(true)
                .setJavaDumpAllThreadsWhiteList(new String[]{"^main$", "^Binder:.*", ".*Finalizer.*"})
                .setJavaDumpAllThreadsCountMax(10)
                .setJavaCallback(callback)
                //------------------------------Java异常输出配置------------------------------
                //------------------------------native异常输出配置------------------------------
                .setNativeRethrow(true)
                .setNativeLogCountMax(10)
                .setNativeDumpAllThreadsWhiteList(new String[]{"^xcrash\\.sample$", "^Signal Catcher$", "^Jit thread pool$", ".*(R|r)ender.*", ".*Chrome.*"})
                .setNativeDumpAllThreadsCountMax(10)
                .setNativeCallback(callback)
                //------------------------------native异常输出配置------------------------------
                //------------------------------ANR异常输出配置------------------------------
                .setAnrRethrow(true)
                .setAnrLogCountMax(10)
                .setAnrCallback(callback)
                //------------------------------ANR异常输出配置------------------------------
                .setPlaceholderCountMax(3)
                .setPlaceholderSizeKb(512)
        );


        Log.d(TAG, "xCrash SDK init: end");


        // Send all pending crash log files.
        //上传日志到服务端，上传成功之后删除
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (File file : TombstoneManager.getAllTombstones()) {
                    sendThenDeleteCrashLog(file.getAbsolutePath(), null);
                }
            }
        }).start();

    }

    private void sendThenDeleteCrashLog(String logPath, String emergency) {
        // Parse
        //Map<String, String> map = TombstoneParser.parse(logPath, emergency);
        //String crashReport = new JSONObject(map).toString();

        // Send the crash report to server-side.
        // ......

        // If the server-side receives successfully, delete the log file.
        //
        // Note: When you use the placeholder file feature,
        //       please always use this method to delete tombstone files.
        //
        //TombstoneManager.deleteTombstone(logPath);
    }


    //存储最新日志到debug.json,方便开发时排查
    private void debug(String logPath, String emergency) {
        // Parse and save the crash info to a JSON file for debugging.
        FileWriter writer = null;
        try {
            File debug = new File(XCrash.getLogDir() + "/debug.json");
            debug.createNewFile();
            writer = new FileWriter(debug, false);
            writer.write(new JSONObject(TombstoneParser.parse(logPath, emergency)).toString());
        } catch (Exception e) {
            Log.d(TAG, "debug failed", e);
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (Exception ignored) {
                }
            }
        }
    }


    //初始化 LeakCanary
    private RefWatcher setupLeakCanary() {
        if (LeakCanary.isInAnalyzerProcess(this)) {
            return RefWatcher.DISABLED;
        }
        return LeakCanary.install(this);
    }

    // 获取监听器
    public static RefWatcher getRefWatcher(Context context) {
        DemoApplication leakApplication = (DemoApplication) context.getApplicationContext();
        return leakApplication.refWatcher;
    }
}
